import re, sys, subprocess, os

# Each test has a header line of the form "<test description> : <options> : <diagnostic>", then an xml document, 
#  ending with a blank line.
# The options may be blank, but the colon is required. The diagnostic is of the form: "<file>:<line>: error: <msg>."
TESTS = """\
badly formed XML : : t.xml:3: error: XML: not well-formed (invalid token).
<?xml version="1.0" encoding="UTF-8" ?>
<machine name="dcdc>
</machine>

illegal name : : t.xml:2: error: bad attribute value name='dc*dc' for element `machine`.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name="dc*dc">
</machine>

illegal nesting : : t.xml:2: error: element `machine` cannot be nested within element `machine`.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'><machine><machine/>
</machine>

bad root element : : t.xml:2: error: unknown root element: `state`.
<?xml version="1.0" encoding="UTF-8" ?>
<state></state>

extra attribute : : t.xml:2: error: extra attribute `foo` for element `machine`.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name="dcdc" foo='bar'>
</machine>

missing attribute : : t.xml:2: error: missing attribute `name` for element `machine`.
<?xml version="1.0" encoding="UTF-8" ?>
<machine >
</machine>

extra content : : t.xml:4: error: content illegal for element `machine`.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
I shouldn't be here!
</machine>

multiple illegal simple elements : : t.xml:4: error: element `init` may not appear more than once as a child of `machine`.
<?xml version="1.0" encoding="UTF-8" ?> 
<machine name='foo'>
<init target='bar'/>
<init target='bar'/>
</machine>

illegal nested simple elements : : t.xml:3: error: element `init` cannot be nested within element `init`.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<init target='bar'><init target='bar'/></init>
</machine>

duplicate transitions no guard : : t.xml:3: error: transition with signature baz[] duplicated.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'> <transition event='baz' target='bar'/>  <transition event='baz' target='bar'/> </state>
</machine>

duplicate transitions with guard : : t.xml:3: error: transition with signature baz[bleurgh()] duplicated.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'> <transition event='baz' guard='bleurgh()' target='bar'/>  <transition event='baz' guard='bleurgh()' target='bar'/> </state>
</machine>

init targets self : : t.xml:3: error: initial transition cannot target self.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'> <init target='bar'/> </state>
</machine>

init targets unknown : : t.xml:3: error: initial transition for state bar targeted an unknown state baz.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'> <init target='baz'/> </state>
</machine>

init targets non substate : : t.xml:3: error: initial transition for state bar targeted a non-substate baz.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'> <init target='baz'/> </state>
<state name='baz'/> 
</machine>

bad transition target : : t.xml:3: error: unknown target state baz for state bar transition bleurgh[].
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'> <transition event='bleurgh' target='baz'/> </state>
</machine>

no initial transition for machine : : t.xml:2: error: machine foo has no initial transition specified on reset.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'/>
<state name='baz'/>
</machine>

machine init targets unknown : : t.xml:4: error: initial transition for machine foo targeted an unknown state baz.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'/>
<init target='baz'/>
</machine>

duplicate state : : t.xml:4: error: duplicate state name bar.
<?xml version="1.0" encoding="UTF-8" ?>
<machine name='foo'>
<state name='bar'/>
<state name='bar'/>
</machine>
"""

failcount, successcount = 0, 0

for test in re.split(r'\n\n', TESTS, flags=re.M):
    lns = test.splitlines()
    try:
        test_description, command_args, expected = [x.strip() for x in lns[0].split(':', 2)]
    except ValueError:
        sys.exit('Expected a test description with 3 colon seperated fields: `%s`.' % lns[0])
    test_xml = '\n'.join(lns[1:])
    sys.stdout.write('Test `%s`... ' % test_description)
    file('t.xml', 'wt').write(test_xml)
    proc = subprocess.Popen('python ../smk.py %s t.xml' % command_args, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    out, err = proc.communicate()
    err = err.strip() # Get rid of trailing newline.
    
    if proc.returncode == 0:    # Check return values for errr.
        failcount += 1
        sys.stdout.write("Fail: expected rc != 0")
    elif err != expected:
        failcount += 1
        sys.stderr.write("Fail:\n exp: %s\n got: %s" % (expected, err))
    else:
        successcount += 1
        sys.stdout.write(" OK.")
    sys.stdout.write("\n")
    
os.unlink('t.xml') 
sys.stdout.write('\n\n\nPassed %d, failed %d.\n' % (successcount, failcount))
    